#include "thread.h"

#include "debug.h"
#include "interrupt.h"
#include "list.h"
#include "memory.h"
#include "print.h"
#include "process.h"
#include "stdint.h"
#include "string.h"
#include "sync.h"

struct task_struct*      main_thread;       // 主线程PCB
struct list              thread_ready_list; // 就绪队列
struct list              thread_all_list;   // 所有任务队列
static struct list_elem* thread_tag; // 用于保存队列中的线程结点

// Mutex for process ID (PID) management.
struct mutex_t pid_lock;

extern void switch_to(struct task_struct* cur, struct task_struct* next);

struct task_struct* thread_start(char* name, int prio, thread_func function,
                                 void* func_arg)
{
  // Allocate memory for the new thread PCB
  struct task_struct* thread = kernel_page_alloc(1);

  // Initialize the thread with the provided attributes.
  init_thread_PCB_info(thread, name, prio);

  // Create the thread with the specified entry point and argument.
  init_thread_stack_func_info(thread, function, func_arg);

  /* 确保之前不在ready队列中 */
  ASSERT(!elem_find(&thread_ready_list, &thread->ready_list_tag));
  /* 加入就绪线程队列 */
  list_append(&thread_ready_list, &thread->ready_list_tag);

  /* 确保之前不在队列中 */
  ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag));
  /* 加入全部线程队列 */
  list_append(&thread_all_list, &thread->all_list_tag);

  return thread;
}

pid_t allocate_pid()
{
  static pid_t next_pid = 0;
  mutex_acquire(&pid_lock);
  next_pid++;
  mutex_release(&pid_lock);
  return next_pid;
}

void init_thread_PCB_info(struct task_struct* pthread, char* name, int priority)
{
  // Clear the memory for the task structure.
  memset(pthread, 0, sizeof(struct task_struct));

  // Assigns a unique process ID (PID)
  pthread->pid = allocate_pid();

  // Set the thread's name, status, priority, stack, and stack magic number.
  strcpy(name, pthread->name);
  pthread->status = TASK_RUNNING;
  pthread->priority = priority;

  // Initializes the pointer to the top of the PCB as the privilege 0 stack
  pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);
  pthread->pg_dir_entry = NULL;
  pthread->stack_magic = 0x19876543;
}

void init_thread_stack_func_info(struct task_struct* pthread,
                                 thread_func function, void* func_arg)
{
  // Allocate space for the interrupt stack and thread stack.
  pthread->self_kstack =
      (uint32_t*)((uint32_t)(pthread->self_kstack) - sizeof(struct intr_stack) -
                  sizeof(struct thread_stack));

  // initialize thread thread_stack

  struct thread_stack* kthread_stack =
      (struct thread_stack*)pthread->self_kstack;

  // Set the entry point, function, and argument for the thread.
  kthread_stack->eip = start_thread_with_func;
  kthread_stack->thead_func = function;
  kthread_stack->func_arg = func_arg;

  // Set certain registers to initial values.
  kthread_stack->ebp = kthread_stack->ebx = kthread_stack->edi =
      kthread_stack->esi = 0;
}

static void make_main_thread()
{
  /* 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000,
      就是为其预留了pthread,地址为0xc009e000,因此不需要通过get_kernel_page另分配一页*/
  main_thread = cur_thread_PCB_get();
  init_thread_PCB_info(main_thread, "main", 31);

  /* main函数是当前线程,当前线程不在thread_ready_list中,
   * 所以只将其加在thread_all_list中. */
  ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag));
  list_append(&thread_all_list, &main_thread->all_list_tag);
}

struct task_struct* cur_thread_PCB_get()
{
  uint32_t esp;
  asm("mov %%esp, %0" : "=g"(esp));
  /* 取esp整数部分即pcb起始地址 */
  return (struct task_struct*)(esp & 0xfffff000);
}

void thread_init()
{
  put_str("thread init start\r\n");

  list_init(&thread_ready_list);
  list_init(&thread_all_list);

  mutex_init(&pid_lock);

  make_main_thread();

  put_str("thread init done!\r\n");
}

void schedule()
{
  ASSERT(intr_get_status() == INTR_OFF);

  struct task_struct* cur_task = cur_thread_PCB_get();
  if (cur_task->status == TASK_RUNNING)
  {
    // add current task to ready list
    list_append(&thread_ready_list, &cur_task->ready_list_tag);
    // reset it's ticks_can_run & status
    cur_task->ticks_can_run = cur_task->priority;
    cur_task->status = TASK_READY;
  }
  else
  {
    // exception
  }

  thread_tag = list_pop(&thread_ready_list);

  struct task_struct* next_thread =
      elem2entry(struct task_struct, ready_list_tag, thread_tag);

  next_thread->status = TASK_RUNNING;

  process_activate(next_thread);

  switch_to(cur_task, next_thread);
}

void thread_block(enum task_status status)
{
  // 检测status是否合法
  ASSERT(((status == TASK_BLOCKED) || (status == TASK_HANGING) ||
          (status == TASK_WAITING)))

  // CPU、PCB等是临界资源，需要保护起来
  enum intr_status old_status = intr_disable();

  struct task_struct* cur = cur_thread_PCB_get();
  cur->status = status;
  schedule();

  intr_set_status(old_status);
}

void thread_unblock(struct task_struct* pthread)
{
  enum intr_status old_status = intr_disable();

  // 检查status是否合法
  ASSERT(((pthread->status == TASK_BLOCKED) ||
          (pthread->status == TASK_HANGING) ||
          (pthread->status == TASK_WAITING)))
  if (pthread->status != TASK_READY)
  {
    // 被换下的进程一定在所有线程队列中，并且不在就绪队列中，否则报错
    ASSERT(!elem_find(&thread_ready_list, &pthread->ready_list_tag));
    if (elem_find(&thread_ready_list, &pthread->ready_list_tag))
    {
      PANIC("thread_unblock: blocked thread in ready_list!");
    }
    // 不论是中断调用的，还是主动调用的，放到最前面能够减少response time
    list_push(&thread_ready_list, &pthread->ready_list_tag);
    pthread->status = TASK_READY;
  }
  intr_set_status(old_status);
}
